function HtmlSerializer() {
    /**对字符编码
     *
     * @param a
     * @returns {XML|string|void}
     */
    function escapeXML (a) {
        return a.replace(/&|<|>|"|'/g,
            function (a) {
                if ("&" == a) return "&amp;";
                if ("<" == a) return "&lt;";
                if (">" == a) return "&gt;";
                if ('"' == a) return "&quot;";
                if ("'" == a) return "&apos;"
            })
    }

    /**特殊字符的解码
     *
     * @param a
     * @returns {XML|string|void}
     */
    function unescapeXML(a) {
        return a.replace(/&(amp|lt|gt|quot|apos);/g,
            function (a) {
                if ("&amp;" == a) return "&";
                if ("&lt;" == a) return "<";
                if ("&gt;" == a) return ">";
                if ("&quot;" == a) return '"';
                if ("&apos;" == a) return "'"
            })
    };

    /**校验
     *
     * @param tagName
     * @param attrB
     * @returns {boolean}
     */
    function checkNodeHasAttr(tagName, attrB) {
        attrB = attrB.toLowerCase();
        if (attrB.match(/^on/i) || attrB.match(/^data-/i) || -1 < HtmlAttrTagArray.indexOf(attrB)) return false;
        switch (tagName) {
            case "a":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < TabIndexAttrArray.indexOf(attrB) || -1 < "charset coords href hreflang name rel rev shape target type".split(" ").indexOf(attrB) ? true : false;
            case "abbr":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "acronym":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "address":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "area":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < TabIndexAttrArray.indexOf(attrB) || -1 < "alt coords href nohref shape target".split(" ").indexOf(attrB) ? true : false;
            case "b":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "bdo":
                return -1 < StyleTitleTagArray.indexOf(attrB) || -1 < ["dir", "lang"].indexOf(attrB) ? true : false;
            case "big":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "blockquote":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["cite"].indexOf(attrB) ? true : false;
            case "br":
                return -1 < StyleTitleTagArray.indexOf(attrB) || -1 < ["clear"].indexOf(attrB) ? true : false;
            case "caption":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "center":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "cite":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "code":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "col":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < ["span", "valign", "width"].indexOf(attrB) ? true : false;
            case "colgroup":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < ["valign"].indexOf(attrB) ? true : false;
            case "dd":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "del":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["cite", "datetime"].indexOf(attrB) ? true : false;
            case "dfn":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "div":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "dl":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["compact"].indexOf(attrB) ? true : false;
            case "dt":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "em":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "font":
                return -1 < StyleTitleTagArray.indexOf(attrB) || -1 < LangTagArray.indexOf(attrB) || -1 < ["color", "face", "size"].indexOf(attrB) ? true : false;
            case "h1":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "h2":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "h3":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "h4":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "h5":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "h6":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "hr":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align", "noshade", "size", "width"].indexOf(attrB) ? true : false;
            case "i":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "img":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < "align alt border height hspace ismap longdesc name src usemap vspace width".split(" ").indexOf(attrB) ? true : false;
            case "ins":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["cite", "datetime"].indexOf(attrB) ? true : false;
            case "kbd":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "li":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["type", "value"].indexOf(attrB) ? true : false;
            case "map":
                return -1 < LangTagArray.indexOf(attrB) || -1 < ["name", "title"].indexOf(attrB) ? true : false;
            case "ol":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["compact", "start", "type"].indexOf(attrB) ? true : false;
            case "p":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["align"].indexOf(attrB) ? true : false;
            case "pre":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["width"].indexOf(attrB) ? true : false;
            case "q":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["cite"].indexOf(attrB) ? true : false;
            case "s":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "samp":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "small":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "span":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "strike":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "strong":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "sub":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "sup":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "table":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < "align bgcolor border cellpadding cellspacing summary width".split(" ").indexOf(attrB) ? true : false;
            case "tbody":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < ["valign"].indexOf(attrB) ? true : false;
            case "td":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < "abbr bgcolor colspan height nowrap rowspan valign width".split(" ").indexOf(attrB) ? true : false;
            case "tfoot":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < ["valign"].indexOf(attrB) ? true : false;
            case "th":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < "abbr bgcolor colspan height nowrap rowspan valign width".split(" ").indexOf(attrB) ? true : false;
            case "thead":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < ["valign"].indexOf(attrB) ? true : false;
            case "tr":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < AlignArray.indexOf(attrB) || -1 < ["bgcolor", "valign"].indexOf(attrB) ? true : false;
            case "tt":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "u":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false;
            case "ul":
                return -1 < StyleTagArray.indexOf(attrB) || -1 < ["compact", "type"].indexOf(attrB) ? true : false;
            case "var":
                return -1 < StyleTagArray.indexOf(attrB) ? true : false
        }
    }

    /**是否存在head等特殊标记
     *
     * @param a
     * @returns {boolean}
     * @constructor
     */
    function getHeadTagIndex(a) {
        a = a.toUpperCase();
        return -1 == HtmlHeadTagArray.indexOf(a)
    }

    /**获得html标签名
     *
     * @param node
     * @returns {string}
     * @constructor
     */
    function getTagNameByNode(node) {
        var nName = node.nodeName,
            b = nName.toUpperCase();
        return "INPUT" == nName && node.type && "image" == node.type.toLowerCase() ? "img" : "BODY" == nName || "HTML" == nName || "FORM" == nName ? "div" : "LABEL" == nName ? "span" : "FIELDSET" == nName ? "div" : "LEGEND" == nName ? "span" : "IFRAME" == nName || "EMBED" == nName ? "div" : "CANVAS" == nName || "VIDEO" == nName ? "img" : "HIGHLIGHT" == nName ? "span" : -1 < Html5TagArray.indexOf(nName) ? "div" : getHeadTagIndex(nName) ? -1 == HtmlTagArray.indexOf(nName) ? "span" : nName.toLowerCase() : nName.toLowerCase()
    }

    /**获得样式
     *
     * @param cssStyleSheet
     * @constructor
     */
    function initCssStyleArray(cssStyleSheet) {
        if (checkIsMedia(cssStyleSheet)) {
            if (RefCssStyleSheetLink.length >= MaxStyleSheetLinkLength) {
                console.log("Hit style cap of " + MaxStyleSheetLinkLength + " styles. Stopping.");
            }
        } else if (!cssStyleSheet.cssRules && cssStyleSheet.href)
            RefCssStyleSheet.push({
                href: cssStyleSheet.href,
                owner: cssStyleSheet.ownerNode
            }),
            StyleArrayCount++,
            RefCssStyleSheetLink.push(cssStyleSheet.href),
            RefCssStyleSheetLink_1.push(cssStyleSheet.href),
            Browser.sendToExtension({
                name: "main_getTextResource",
                href: cssStyleSheet.href
            });
        else {
            var cssRule = cssStyleSheet.cssRules,
                c = null;
            for (e = 0; e < cssRule.length; e++)
                if (cssRule[e].type == CSSRule.IMPORT_RULE) {
                    if (cssRule[e].styleSheet) {
                        var url = cssRule[e].styleSheet.href;
                        /^https?:\/\//.test(cssRule[e].href)
                        || /^\/\//.test(cssRule[e].href)
                        || c
                        && (url = "/" === cssRule[e].href[0] ?
                                    c + cssRule[e].href :
                                    c + "/" + cssRule[e].href);
                        -1 == RefCssStyleSheetLink.indexOf(url) &&
                        (
                            RefCssStyleSheet.push({
                                href: url,
                                owner: cssStyleSheet.ownerNode
                            }),
                                StyleArrayCount++,
                                RefCssStyleSheetLink.push(url),
                                RefCssStyleSheetLink_1.push(url),
                                Browser.sendToExtension({
                                    name: "main_getTextResource",
                                    href: url
                                })
                        )
                    }
                } else {
                    cssRule[e].type == CSSRule.MEDIA_RULE
                    && checkIsMedia(cssRule[e])
                    && RefCssStyleSheet.push(cssRule[e]);
                }
            RefCssStyleSheet.push(cssStyleSheet)
        }
    }

    /**获得资源的url
     *
     * @param a
     * @param b
     * @param c
     * @returns {string}
     */
    function getResourceUrl(a, b, c) {
        c = c.trim();
        a = c.match(/^http/i) ? c : c.match(/^\//) ?
                a.replace(/^(.*?:\/\/[^\/]+).*$/, "$1") + c :
                a.replace(/^(.*\/)/, "$1") + c;
        return "url('" + a + "')"
    }


    function checkIsMedia(cssStyleSheet) {
        if (cssStyleSheet.media && cssStyleSheet.media.length) {
            for (var i = 0; i < cssStyleSheet.media.length; i++) {
                var media = cssStyleSheet.media[i].toLowerCase();
                if (media.match(/\bscreen\b/i) || media.match(/\ball\b/i)) return true
            }
            return false
        }
        return true
    }

    /**获得样式属性
     *
     * @param selectContainer
     * @returns {string}
     * @constructor
     */
    function getCssRuleByRef(selectContainer) {
        setTimeout(function(){
            try{
                CssRuleArray = [];
                for(var i=0;i< RefCssStyleSheet.length;i++){
                    var temp = RefCssStyleSheet[i];
                    if(temp.cssRules){
                        for(var j=0;j<temp.cssRules.length;j++){
                            var cssRule = temp.cssRules[j];
                            if(cssRule.selectorText && cssRule.selectorText.match(/(:?:before)|(:?:after)/)){
                                CssRuleArray.push(cssRule);
                            }
                        }
                    }
                }
                SelectContainerArray = [];
                SelectContainerArray.push({
                    element:selectContainer,
                    string :"",
                    i:0,
                    after:null
                });
                serializing();
            }catch(e){
                endSerializer(null,e);
            }
        },300);
    }

    /**对html进行编码转换
     *
     * @param htmlStr
     * @returns {string}
     * @constructor
     */
    function encodingHtml1(htmlStr) {
        return escapeXML(htmlStr)
    }

    /** 获得Div节点的html代码
     *
     * @param node
     */
    function getDivNodeHtmlStr(node) {
        if ("iframe" != node.nodeName.toLowerCase())
            return null;
        var b = "";
        if (node.dataset && node.dataset.en_id && P && P[node.dataset.en_id]) {
            var b = P[node.dataset.en_id],
                c = getNodeStyle(node);
            node.width && (c.map.width = {
                value: node.width + "px"
            });
            node.height && (c.map.height = {
                value: node.height + "px"
            });
            c.map.position && "static" !== c.map.position.value || (c.map.position = {
                value: "relative"
            });
            node = "";
            for (var e in c.map)
                node += e + ":" + c.map[e].value + ";";
            node = node ? ' style="' + encodingHtml2(node) + '"' : "";
            return "<div" + node + ">" + b + "</div>"
        }
        return null
    }

    /**是否自定义列表节点
     *
     * @param node
     */
    function isDLNode(node) {
        if (node.parentNode && node != SelectCommonAncestorContainer) {
            var nName = node.parentNode.nodeName;
            nName = nName.toLowerCase();
            node = getTagNameByNode(node);
            if(nName == "dl" && node!="dd" && node!="dt") {
                return false;
            }
            //if ("dl" == nName && (node = getTagNameByNode(node), "dd" != node && "dt" != node))
            //    return false
        }
        return true
    }

    /**序列化操作
     *
     */
    function serializing() {
        SeriaCount++;
        if (0 == SeriaCount % 500)
            setTimeout(function () {
                try {
                    serializing()
                } catch (a) {
                    endSerializer(null, a)
                }
            },
            25);
        else {
            //所选择的 区域 的根节点
            var selectContainer = SelectContainerArray[SelectContainerArray.length - 1];
            if(!selectContainer) endSerializer("");

            //加载样式
            if (selectContainer.i == 0) {
                //过滤掉无效标签
                if (!getHeadTagIndex(getTagNameByNode(selectContainer.element))) {
                    SelectContainerArray.pop();
                    serializing();
                    return
                }
                //过滤掉自定义列表标签
                if (!isDLNode(selectContainer.element)) {
                    console.log('discarding invalid DL child "' + selectContainer.element.nodeName + '"');
                    SelectContainerArray.pop();
                    serializing();
                    return
                }

                //根节点
                if (SelectionRange && selectContainer.element !=
                    SelectionRange.commonAncestorContainer && !SelectionRange.intersectsNode(selectContainer.element)) {
                    SelectContainerArray.pop();
                    serializing();
                    return
                }

                if (SelectionRange && selectContainer.element ===
                    SelectionRange.endContainer && SelectionRange.endOffset === 0) {
                    SelectContainerArray.pop();
                    serializing();
                    return
                }
                var htmlStrTemp;
                //获取节点的html
                if (htmlStrTemp = getDivNodeHtmlStr(selectContainer.element)) {
                    selectContainer.string += htmlStrTemp;
                    console.log("line 516 "+selectContainer.string);
                    SelectContainerArray.pop();
                    if(SelectContainerArray.length){
                        SelectContainerArray[SelectContainerArray.length - 1].string = selectContainer.string;
                        serializing();
                    }else {
                        endSerializer(selectContainer.string);
                    }
                    return
                }
                htmlStrTemp = {};
                //导入样式
                if(IsIncludeCss) {
                    //getYouTubeVideo(selectContainer.element);
                    htmlStrTemp = getNodeStyle(selectContainer.element);
                    if(htmlStrTemp.after) selectContainer.after = htmlStrTemp.after;
                }
                if (htmlStrTemp.map && htmlStrTemp.map.display && "none" == htmlStrTemp.map.display.value) {
                    SelectContainerArray.pop();
                    serializing();
                    return
                }
                var tagName = getTagNameByNode(selectContainer.element);
                selectContainer.string += "<" + tagName;
                setNodeAttrWH(selectContainer.element);
                //Canvas 和 video 节点 做特殊处理
                if ("CANVAS" == selectContainer.element.nodeName) {
                    try {
                        selectContainer.string += ' src="' + selectContainer.element.toDataURL() + '"'
                    } catch (e) {
                    }
                }
                else if("VIDEO" == selectContainer.element.nodeName && selectContainer.element.poster) {
                    selectContainer.string += ' src="' + escapeXML(selectContainer.element.poster) + '"';
                }

                //对标记的url做特殊处理
                if (selectContainer.element.attributes && selectContainer.element.attributes.length) {
                    for (selectContainer.i = 0; selectContainer.i < selectContainer.element.attributes.length; selectContainer.i++) {
                        !checkNodeHasAttr(tagName, selectContainer.element.attributes[selectContainer.i].name)
                        || "VIDEO" == selectContainer.element.nodeName
                        && "src" == selectContainer.element.attributes[selectContainer.i].name
                        || (selectContainer.string += " " + getUrlOrImgDataAttr(selectContainer.element, selectContainer.element.attributes[selectContainer.i]));
                    }
                }
                if(IsIncludeCss) {
                    selectContainer.string += htmlStrTemp.style;
                }
                selectContainer.string += ">";
                if(IsIncludeCss && htmlStrTemp.before){
                     selectContainer.string += htmlStrTemp.before;
                }
                selectContainer.i = 0
            }
            //对选择的根节点的子节点做过滤
            for (; selectContainer.i < selectContainer.element.childNodes.length;) {
                //文本节点
                if (selectContainer.element.childNodes[selectContainer.i].nodeType == Node.TEXT_NODE)
                    htmlStrTemp = (SelectionRange && selectContainer.element.childNodes[selectContainer.i] === SelectionRange.startContainer)
                        ? encodingHtml1(selectContainer.element.childNodes[selectContainer.i].textContent.substr(SelectionRange.startOffset))
                        : SelectionRange && selectContainer.element.childNodes[selectContainer.i] === SelectionRange.endContainer
                            ? encodingHtml1(selectContainer.element.childNodes[selectContainer.i].textContent.substr(0, SelectionRange.endOffset))
                            : SelectionRange && !SelectionRange.intersectsNode(selectContainer.element.childNodes[selectContainer.i])
                                ? ""
                                : encodingHtml1(selectContainer.element.childNodes[selectContainer.i].textContent),
                    selectContainer.string += htmlStrTemp;
                else {
                    //元素节点 非Video节点 则将节点添加到Container数组
                    if (htmlStrTemp = selectContainer.element.childNodes[selectContainer.i].nodeType == Node.ELEMENT_NODE)
                        htmlStrTemp = "VIDEO" == selectContainer.element.nodeName ? false : true;
                    if (htmlStrTemp) {
                        SelectContainerArray.push({
                            element: selectContainer.element.childNodes[selectContainer.i],
                            string: selectContainer.string,
                            i: 0,
                            after: null
                        });
                        selectContainer.i++;
                        serializing();
                        return
                    }
                }
                selectContainer.i++
            }
            if(IsIncludeCss && selectContainer.after){
                selectContainer.string += selectContainer.after;
            }
            selectContainer.string += "</" + getTagNameByNode(selectContainer.element) + ">";
            SelectContainerArray.pop();
            if(SelectContainerArray.length){
                SelectContainerArray[SelectContainerArray.length - 1].string = selectContainer.string;
                serializing();
            }else {
                endSerializer(selectContainer.string);
            }
        }
    }

    /**
     *
     * @param cssText
     */
    function getCssArray(cssText) {
        var b = {};
        cssText = cssText.split(/;(?!.[^\(]+?\))\s*/);
        for (var c = 0; c < cssText.length; c++) if (cssText[c] = cssText[c].trim(), cssText[c]) {
            var e = cssText[c].indexOf(":"),
                d = cssText[c].substr(0, e).trim(),
                e = cssText[c].substr(e + 1).trim();
            if (d && e) {
                for (var H = /(url\(.+?\))/g,
                         l = H.exec(e), p = e; l;) {
                    var m = l = l[1];
                    /url\(.+?(data:.+?;.+?,.+?)\)/.test(m) && (m = "url(" + /url\(.+?(data:.+?;.+?,.+?)\)/.exec(m)[1] + ")");
                    /url\(\/\/(.+?)\)/.test(m) && (m = "url(http://" + /url\(\/\/(.+?)\)/.exec(m)[1] + ")");
                    /url\((.+?)\)/.test(m) && (m = "url(" + escapeXML(unescapeXML(/url\((.+?)\)/.exec(m)[1])) + ")");
                    p = p.replace(l, m);
                    l = H.exec(e)
                }
                e = p;
                b[d.toLowerCase()] = e
            }
        }
        if (b["background-position"] && b["background-repeat"] && "0px 50%" == b["background-position"].trim() && "initial initial" == b["background-repeat"].trim()) {
            for (var h in b) h.match(/background/) && delete b[h];
            b.background = "0"
        }
        return b
    }

    /**获取节点的styleArray
     *
     * @param node
     * @returns {{}}
     */
    function getNodeStyleArray(node) {
        var b = {};
        if (node.style.cssText) {
            node.style.savedCssObj || (node.style.savedCssObj = getCssArray(node.style.cssText));
            node = node.style.savedCssObj;
            for (var c in node)
                b[c] = node[c]
        }
        return b
    }

    /**设置node的宽高属性
     *
     * @param node
     */
    function setNodeAttrWH(node) {
        "img" == node.nodeName.toLowerCase() && (node.attributes.width || node.setAttribute("width", node.width), node.attributes.height || node.setAttribute("height", node.height))
    }

    /**设置node的背景
     *
     * @param node
     */
    function setNodeBackground(node) {
        node.style && (node.hasAttribute("background") && (node.style.backgroundImage = "url(" + node.getAttribute("background") + ")"), node.hasAttribute("bgcolor") && (node.style.backgroundColor = node.getAttribute("bgcolor")), node.hasAttribute("text") && (node.style.color = node.getAttribute("text")))
    }

    /**
     *
     * @param a
     * @returns {number}
     */
    function checkNodeAttr(a) {
        var b = {
                ids: {
                    regex: /#[A-Z]+/ig,
                    count: 0
                },
                classes: {
                    regex: /\.[A-Z]+/ig,
                    count: 0
                },
                attrs: {
                    regex: /\[.*?\]/g,
                    count: 0
                },
                pseudos: {
                    regex: /:+[A-Z]+/ig,
                    count: 0
                },
                pseudoEls: {
                    regex: /:+(first-line|first-letter|before|after)/ig,
                    count: 0
                },
                types: {
                    regex: /(^|\s)[A-Z]+/ig,
                    count: 0
                }
            },
            c;
        for (c in b)
            for (var e = b[c].regex; e.exec(a) ;)
                b[c].count++;
        b.pseudoClasses = {};
        b.pseudoClasses.count = b.pseudos.count - b.pseudoEls.count;
        return 65536 * b.ids.count + 256 * (b.classes.count + b.attrs.count + b.pseudoClasses.count) + (b.types.count + b.pseudoEls.count)
    }

    /**获取选择器名称
     * 例如
     *  .div #footer
     * @param selectText
     * @returns {Array}
     */
    function getSecetorText(selectText) {
        for (var b = [], i = 0, e = 0, d = "", e = 0; e < selectText.length; e++)
            d ? selectText[e] == d && (d = "") :
                "'" == selectText[e] || '"' == selectText[e]
                    ? d = selectText[e]
                    : "," == selectText[e] && (b.push(selectText.substring(i, e).trim()), i = e + 1);
        b.push(selectText.substr(i).trim());
        return b
    }

    function setNodeFont(node, b) {
        var c = node.nodeName.toLowerCase();
        "table" != c && "caption" != c || "CSS1Compat" != SelectWindow.document.compatMode || (b["font-size"] = {
            value: "inherit",
            score: 0
        },
            b["font-weight"] = {
                value: "inherit",
                score: 0
            },
            b["font-style"] = {
                value: "inherit",
                score: 0
            },
            b["font-variant"] = {
                value: "inherit",
                score: 0
            })
    }
    /** 对节点的盒子边框间距操作
     *
     * @param a
     * @param attrArray
     * @constructor
     */
    function delDivPaddingAndMargin(a, attrArray) {
        "padding" == a ? (delete attrArray["padding-top"], delete attrArray["padding-bottom"], delete attrArray["padding-left"], delete attrArray["padding-right"]) :
            "margin" == a ? (delete attrArray["margin-top"], delete attrArray["margin-bottom"], delete attrArray["margin-left"], delete attrArray["margin-right"]) :
                "background" == a ? (delete attrArray["background-color"], delete attrArray["background-position"], delete attrArray["background-size"], delete attrArray["background-repeat"], delete attrArray["background-origin"], delete attrArray["background-clip"], delete attrArray["background-attachment"], delete attrArray["background-image"], delDivPaddingAndMargin("background-position", attrArray), delDivPaddingAndMargin("background-repeat", attrArray)) :
                    "background-position" == a ? (delete attrArray["background-position-x"], delete attrArray["background-position-y"]) :
                        "background-repeat" == a ? (delete attrArray["background-repeat-x"], delete attrArray["background-repeat-y"]) :
                            "border" == a ? (delete attrArray["border-width"], delete attrArray["border-style"], delete attrArray["border-color"], delDivPaddingAndMargin("border-top", attrArray), delDivPaddingAndMargin("border-right", attrArray), delDivPaddingAndMargin("border-bottom", attrArray), delDivPaddingAndMargin("border-left", attrArray)) :
                                "border-top" == a ? (delete attrArray["border-top-width"], delete attrArray["border-top-style"], delete attrArray["border-top-color"]) :
                                    "border-right" == a ? (delete attrArray["border-right-width"], delete attrArray["border-right-style"], delete attrArray["border-right-color"]) :
                                        "border-bottom" == a ? (delete attrArray["border-bottom-width"], delete attrArray["border-bottom-style"], delete attrArray["border-bottom-color"]) :
                                        "border-left" == a && (delete attrArray["border-left-width"], delete attrArray["border-left-style"], delete attrArray["border-left-color"])
    }

    /**获取节点的样式
     *
     * @param node
     * @param attrArray
     * @return
     */
    function getNodeStyle(node, attrArray) {
        setNodeBackground(node);
        var styleStr = "",
            e = null,
            before = {},
            after = {};
        node.attributes && node.attributes.style && (e = getCssArray(node.attributes.style.value));
        var map = {};
        setNodeFont(node, map);
        var p;
        if ((p = SelectWindow.getMatchedCSSRules(node)) && p.length) for (var m = 0; m < p.length; m++) {
            var t = 0,
                f = false;
            p[m].selectorText.match(/:visited/i) && (f = true);
            for (var g = getSecetorText(p[m].selectorText), k = 0; k < g.length; k++) {
                var u;
                try {
                    u = node.webkitMatchesSelector(g[k])
                } catch (x) {
                    console.log("Couldn't match against selector " + g[k] + " in: " + p[m].selectorText),
                        log.exception(x)
                }
                if (u) {
                    var f = false,
                        s = checkNodeAttr(g[k]);
                    s >= t && (t = s)
                }
            }
            if (!f) {
                s = getNodeStyleArray(p[m]);
                if (s["counter-reset"] && (f = s["counter-reset"].split(/\s+/), 0 < f.length)) for (g = 0; g < f.length;) / ^\d +$ /.test(f[g + 1]) ? (w[f[g]] = parseInt(f[g + 1]), g += 2) : w[f[g++]] = 0;
                if (s["counter-increment"] && (f = s["counter-increment"].split(/\s+/), 0 < f.length)) for (g = 0; g < f.length;) w[f[g]] || (w[f[g]] = 0),
                    /^\d+$/.test(f[g + 1]) ? (w[f[g]] += parseInt(f[g + 1]), g += 2) : w[f[g++]]++;
                for (var n in s) f = n.replace(/^-/, "").replace(/-[a-z]/g,
                    function (a) {
                        return a[1].toUpperCase()
                    }),
                p[m].style[f] && "counterIncrement" !== f && "counterReset" !== f && (f = 0, map[n] && (f = map[n].score), g = t, s[n].match(/!important\s*$/i) && (g += 16777216, s[n] = s[n].replace(/\s*!important\s*$/i, "")), g >= f && (delDivPaddingAndMargin(n, map), map[n] = {
                    value: s[n],
                    score: g
                }))
            }
        }
        for (m = 0; m < CssRuleArray.length; m++) {
            u = CssRuleArray[m];
            var v;
            if (node.webkitMatchesSelector)
                try {
                    v = node.webkitMatchesSelector(u.selectorText.replace(/(:?:before)|(:?:after)/g, ""))
                } catch (z){}
            if (v) {
                t = p = false;
                u.selectorText.match(/:?:before/) && (p = true);
                u.selectorText.match(/:?:after/) && (t = true);
                for (n in map)
                    p && fa[n.toLowerCase()] && (before[n] = map[n].value),
                    t && fa[n.toLowerCase()] && (after[n] = map[n].value);
                u = getNodeStyleArray(u);
                for (n in u)
                    p && (before[n] = u[n]),
                    t && (after[n] = u[n])
            }
        }
        p = [before, after];
        for (t = 0; t < p.length; t++) {
            u = p[t];
            var textStr = s = "",
                y = 0;
            if (u["counter-reset"])
                for (f = u["counter-reset"].split(/\s+/), g = 0; g < f.length;)
                    / ^\d +$ /.test(f[g + 1]) ? (w[f[g]] = parseInt(f[g + 1]), g += 2) : w[f[g++]] = 0;
            if (u["counter-increment"])
                for (f = u["counter-increment"].split(/\s+/), g = 0; g < f.length;)
                    w[f[g]] || (w[f[g]] = 0),
                    /^\d+$/.test(f[g + 1]) ? (w[f[g]] += parseInt(f[g + 1]), g += 2) : w[f[g++]]++;
            for (k in u)
                "content" == k ?
                    (textStr = u[k], textStr = textStr.trim(), textStr = textStr.replace(/\s+!important$/, ""), "none" == textStr && (textStr = '""'), textStr.match(/^'/) ?
                        textStr = textStr.replace(/^'(.*?)'.*/, "$1") :
                        textStr.match(/^"/) && (textStr = textStr.replace(/^"(.*?)".*/, "$1")), textStr.match(/^url\((.*)\)/) ?
                            textStr = "<img src='" + textStr.match(/^url\((.*)\)/)[1] + "'></img>" :
                            textStr.match(/^\s*counter\(.*?\)/) ?
                                (f = textStr.match(/^\s*counter\((.*?)\)/)[1].split(",")[0], "number" === typeof w[f] && (textStr = escapeXML("" + w[f]))) :
                                textStr.match(/^\s*attr\((.*?)\)/) ?
                                    (f = textStr.match(/^\s*attr\((.*?)\)/)[1], textStr = node.getAttribute(f) ?
                                        escapeXML("" + node.getAttribute(f)) : "") :
                                        textStr.match(/^\s*-webkit-image-set\(.+\)/) && (s += "background:" + textStr + ";", textStr = "")) :
                    "counter-reset" !== k && "counter-increment" !== k && (s += k + ":" + u[k] + ";"),
                y++;
            y && (s = '<span style="' + s + '">' + textStr + "</span>", 0 == t ? before = s : after = s)
        }
        "string" != typeof before && (before = null);
        "string" != typeof after && (after = null);
        node == SelectCommonAncestorContainer && (attrArray = HtmlAttrArray);
        //删除属性
        if (attrArray)
            for (m = 0; m < attrArray.length; m++)
                map[attrArray[m]] && delete map[attrArray[m]];
        setHeightAttr(node, map);
        delCompAttr(map);
        if (e)
            for (n in k = /url\(['"]?(.*?)['"]?\)/i, e)
                k.test(e[n]) && (p = k.exec(e[n])[1], t = SelectWindow.document.location.href, Q && Q.href && (t = Q.href), t = getResourceUrl(t, v, p), e[n] = e[n].replace('url("' + p + '")', t).replace("url('" + p + "')", t).replace("url(" + p + ")", t)),
                map[n] = {
                    value: e[n]
                };
        for (m in map)
            styleStr += m + ":" + map[m].value + ";";
        styleStr = styleStr ? ' style="' + encodingHtml2(styleStr) + '"' : "";
        return {
            style: styleStr,
            before: before,
            after: after,
            map: map
        }
    }

    /**设置高
     *
     * @param a
     * @param b
     */
    function setHeightAttr(a, b) {
        if (b.height && b.height.value.match(/%$/)) {
            var c = b.height.value;
            try {
                "fixed" != SelectWindow.getComputedStyle(a).position && (SelectWindow.getComputedStyle(a.parentNode).height || (c = "auto"))
            } catch (e) { }
            b.height.value = c
        }
    }

    /**删除兼容的属性
     *
     * @param attrArray
     */
    function delCompAttr(attrArray) {
        for (var b = "-webkit-user-select -moz-user-select -ms-user-select user-select -webkit-user-modify -moz-user-modify -ms-user-modify user-modify".split(" "), i = 0; i < b; i++)
            attrArray[b[i]] && delete attrArray[b[i]]
    }

    /**对node的url 转换为链接或者图片data
     *
     * @param url
     * @param attr
     * @returns {string}
     */
    function getUrlOrImgDataAttr(url, attr) {
        var val = null;

        //
        //
        //"href" == attr.name.toLowerCase() ?
        //    ((val = url.href.animVal ? url.href.animVal :
        //        url.href) && val.match(/^javascript/i) ?
        //        val = "#" :
        //    /^https?:\/\//.test(val) || (val = url.baseURI + val), val = encodingHtml2(val)) :
        //"src" == attr.name.toLowerCase() &&
        //(val = url.src, "" == attr.value.trim() && (val = ""),
        //val && val.match(/^javascript/i) && (val = "#"),
        //    val = convertImgSrcToBase64(url, val),
        //    val = encodingHtml2(val));
        //null === val && (val = encodingHtml2(attr.value.replace(/\u000A/g, "")));
        //return attr.name + '="' + val + '"'

        if(attr.name.toLowerCase()==="href"){
            val = url.href.animVal ? url.href.animVal : url.href;
            if(val && val.match(/^javascript/i)) {
                val = "#";
            }else {
                if(!/^https?:\/\//.test(val))
                    val = url.baseURI + val;
                val = encodingHtml2(val);
            }
        }else if(attr.name.toLowerCase()==="src"){
            val = url.src;
            "" == attr.value.trim() && (val = "");
            val && val.match(/^javascript/i) && (val = "#");
            val = convertImgSrcToBase64(url, val);
            val = encodingHtml2(val);
        }

        return attr.name + '="' + val+'"';


    }

    /**将图片转换为base64的src
     *
     * @param node
     * @param urlVal
     * @param c
     * @returns {*}
     */
    function convertImgSrcToBase64(node, urlVal, c) {
        if (!node) return urlVal;
        var e = SelectWindow || window;
        //图片节点 非动图
        if ("IMG" == node.nodeName
            && /^https?:\/\//i.test(urlVal)
            && !/\.gif($|\?)/i.test(urlVal)
            && (node.naturalWidth || node.naturalHeight)) {
            //如果图片不在当前url的同服务器上的话，则无法保存图片的DataURL
            if (/^(https?:\/\/.[^\/]+)\/?/.exec(urlVal)[1] === e.document.location.origin) {
                e = e.document.createElement("canvas");
                e.width = (c ? node.naturalWidth : node.width) || 1;
                e.height = (c ? node.naturalHeight : node.height) || 1;
                e.getContext("2d").drawImage(node, 0, 0, c ? node.naturalWidth : node.width, c ? node.naturalHeight : node.height);
                try {
                    return /\.jpe?g$/.test(urlVal) ? e.toDataURL("image/jpeg") : e.toDataURL("image/png")
                } catch (d) {
                    if (18 != d.code)
                        throw d;
                    return urlVal
                } finally {
                }
            } else
                return urlVal;
        }
        else
            return node.naturalWidth && node.naturalHeight ? urlVal : ""
    }

    function convertImgSrcToBase64OnUrl(node,urlVal){
        var e = window;
        if (/^(https?:\/\/.[^\/]+)\/?/.exec(urlVal)[1] === e.document.location.origin) {
            e = e.document.createElement("canvas");
            e.width = (c ? node.naturalWidth : node.width) || 1;
            e.height = (c ? node.naturalHeight : node.height) || 1;
            e.getContext("2d").drawImage(node, 0, 0, c ? node.naturalWidth : node.width, c ? node.naturalHeight : node.height);
            try {
                return /\.jpe?g$/.test(urlVal) ? e.toDataURL("image/jpeg") : e.toDataURL("image/png")
            } catch (d) {
                if (18 != d.code)
                    throw d;
                return urlVal
            } finally {
            }
        } else
            return urlVal;
    }

    /**结束序列化
     *
     * @param a
     * @param errorMsg
     */
    function endSerializer(a, errorMsg) {
        X = false;
        if (!errorMsg) {
            var c = "",
                e = "";
            //增加默认属性
            if (IsIncludeCss) {
                for (var e = c = "",
                         d = SelectCommonAncestorContainer.parentNode; d && d.parentNode;) {
                    var h = getNodeStyle(d, HtmlAttrArray).style,
                        l = getTagNameByNode(d);
                    getHeadTagIndex(l) && (c = "<" + l + h + ">" + c, e = e + "</" + l + ">");
                    d = d.parentNode
                }
                c = '<div style="font-size: 16px">' + c;
                e += "</div>"
            }
            a = c + a + e
        }
        //移除引用
        for (e = 0; e < N.length; e++)
            N[e].parentNode.removeChild(N[e]);
        N = [];
        for (e = 0; e < EventArray.length; e++)
            try {
                EventArray[e](a, errorMsg)
            } catch (k) {
                console.log("Couldn't run 'serialize' callback: " + k.stack || k.trace)
            }
        EventArray = []
    }
    function encodingHtml2(htmlStr) {
        return htmlStr ? escapeXML(htmlStr) : ""
    }
    var RefCssStyleSheetLink_1 = [],
        w = {},
        StyleArrayCount = 0,
        RefCssStyleSheet = [],
        SelectCommonAncestorContainer,
        SelectionRange,
        IsIncludeCss,
        EventArray = [],
        N = [],
        SeriaCount = 0,
        SelectContainerArray = [],
        X = false,
        CssRuleArray = [],
        P,
        SelectWindow,
        Q = null,
        MaxStyleSheetLinkLength = 100,
        RefCssStyleSheetLink = [],
        HtmlTagArray = "A ABBR ACRONYM ADDRESS AREA B BDO BIG BLOCKQUOTE BR CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIV DL DT EM FONT H1 H2 H3 H4 H5 H6 HR I IMG INS KBD LI MAP OL P PRE Q S SAMP SMALL SPAN STRIKE STRONG SUB SUP TABLE TBODY TD TFOOT TH THEAD TR TT U UL VAR".split(" "),
        Html5TagArray = "ARTICLE ASIDE DETAILS FOOTER FIGURE FIGCAPTION HEADER HGROUP NAV SECTION SUMMARY".split(" "),
        HtmlHeadTagArray = "APPLET BASE BASEFONT BGSOUND BLINK BODY BUTTON DIR EMBED FIELDSET FORM FRAME FRAMESET HEAD HTML IFRAME ILAYER INPUT ISINDEX LABEL LAYER LEGEND LINK MARQUEE MENU META NOEMBED NOFRAMES NOSCRIPT OBJECT OPTGROUP OPTION PARAM PLAINTEXT SCRIPT SELECT STYLE TEXTAREA TITLE XML".split(" "),
        HtmlAttrTagArray = "id class accesskey data dynsrc tabindex style".split(" "),
        StyleTitleTagArray = ["style", "title"],
        LangTagArray = ["lang", "xml:lang", "dir"],
        TabIndexAttrArray = ["accesskey", "tabindex"],
        StyleTagArray = ["style", "title", "lang", "xml:lang", "dir"],
        AlignArray = ["align", "char", "charoff"],
        HtmlAttrArray = "border border-bottom border-bottom-color border-bottom-style border-bottom-width border-collapse border-color border-left border-left-color border-left-style border-left-width border-right border-right-color border-right-style border-right-width border-spacing border-style border-top border-top-color border-top-style border-top-width border-width bottom clear display float height layout-flow layout-grid layout-grid-char layout-grid-char-spacing layout-grid-line layout-grid-mode layout-grid-type left margin margin-bottom margin-left margin-right margin-top max-height max-width min-height min-width padding padding-bottom padding-left padding-right padding-top position right size table-layout top visibility width z-index".split(" ");

    var L = [],
        C = 0,
        fa = {
            "border-collapse": true,
            "border-spacing": true,
            "caption-side": true,
            color: true,
            cursor: true,
            direction: true,
            "empty-cells": true,
            "font-family": true,
            "font-size": true,
            "font-style": true,
            "font-variant": true,
            "font-weight": true,
            font: true,
            "letter-spacing": true,
            "line-height": true,
            "list-style-image": true,
            "list-style-position": true,
            "list-style-type": true,
            "list-style": true,
            orphans: true,
            quotes: true,
            "text-align": true,
            "text-indent": true,
            "text-transform": true,
            visibility: true,
            "white-space": true,
            widows: true,
            "word-spacing": true
        };
    this.convertImgSrcToBase64IfPossible = convertImgSrcToBase64;
    this.convertImgSrcToBase64 = convertImgSrcToBase64OnUrl;

    /** html序列化
     *
     * @param a
     * @param b selectionRange
     * @param isIncludeCss
     * @param callback d(returnHtml,returnError)
     * @param h
     * @param v
     */
    this.serialize = function (commonAncestorContainer , selectionRange, isIncludeCss, callback, h, v) {
        try {
            if ((SelectWindow = v) || (SelectWindow = window), callback && EventArray.push(callback), X) {
                console.log("Called serialize while blocked. Added callback but won't change base element.");
            } else {
                X = true;
                SelectCommonAncestorContainer = commonAncestorContainer ;
                SelectionRange = selectionRange;
                IsIncludeCss = isIncludeCss;
                P = h;
                StyleArrayCount = 0;
                RefCssStyleSheetLink = [];
                w = {};
                Q = document.getElementsByTagName("base")[0];
                var a;
                for (a = 0; a < SelectWindow.document.styleSheets.length; a++) {
                    initCssStyleArray(SelectWindow.document.styleSheets[a]);
                }
                0 == StyleArrayCount && getCssRuleByRef(SelectCommonAncestorContainer)
            }
        } catch (l) {
            endSerializer(null, l)
        }
    };
    Object.preventExtensions(this)
}
Object.preventExtensions(HtmlSerializer);